package middleware

import (
	"WebServer/utools"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go/v4"
	"github.com/gofiber/fiber/v2"
	"os"
	"time"
)

var key []byte

type Claims struct {
	Data string `json:"username"`
	jwt.StandardClaims
}

type TokenInterface struct {
	plugin []func(ctx *fiber.Ctx, token, origin string) error
	path   []string
}

func (token *TokenInterface) Use(fun func(ctx *fiber.Ctx, token, origin string) error) {
	token.plugin = append(token.plugin, fun)
}

func (token *TokenInterface) AddPath(path ...string) {
	token.path = append(token.path, path...)
}

func (token *TokenInterface) Handler() fiber.Handler {
	return func(ctx *fiber.Ctx) error {
		tokenCheck := Token(token.path...)
		relToken, err := tokenCheck(ctx)
		if err != nil {
			res, _ := utools.Message.Error(err.Error(), ctx)
			return res
		} else {
			if len(relToken) > 0 {
				for i := 0; i < len(token.plugin); i++ {
					pluginFunc := token.plugin[i]
					err := pluginFunc(ctx, relToken, ctx.Get("token"))
					if err != nil {
						res, _ := utools.Message.Error(err.Error(), ctx)
						return res
					}
				}
			}
		}
		ctx.Next()
		return nil
	}
}

func init() {
	nowPath, _ := os.Getwd()
	keyPath := fmt.Sprint(nowPath, "\\keys\\JWT.key")
	file, err := os.ReadFile(keyPath)
	if err != nil {
		println("token key read file,please check about file path:", keyPath, err.Error())
	}
	baseKey, err := base64.StdEncoding.DecodeString(string(file))
	if err != nil {
		println("The key resolution fail")
	}
	key = baseKey
}

func Token(targetPath ...string) func(ctx *fiber.Ctx) (string, error) {
	return func(ctx *fiber.Ctx) (string, error) {
		wantTo := ctx.Path()
		if utools.Include(targetPath, wantTo) {
			token := ctx.Get("token")
			if len(token) > 0 {
				res, err := ParsingToken(token)
				if err != nil {
					return "", errors.New("error token")
				} else {
					ctx.Set("relToken", res) //存储key为relToken共后续处理器使用
					ctx.Set("originToken", token)
					return res, nil
				}
			} else {
				return "", errors.New(fmt.Sprint("The api ", wantTo, " want to the correct verification token"))
			}
		}
		ctx.Next()
		return "", nil
	}
}

func NewToken() *TokenInterface {
	return &TokenInterface{}
}

func MakeToken(data string) (string, error) {
	nowTime := time.Now()
	failTime := nowTime.Add(7 * (24 * time.Hour))
	claims := Claims{
		Data: data,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: &jwt.Time{Time: failTime}, // 失效时间 一周
			Issuer:    "ROOT",                    // 签发人
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(key)
	if err != nil {
		return "", err
	} else {
		return tokenString, nil
	}
}

func ParsingToken(token string) (string, error) {
	parseToken, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return key, nil
	})
	if parseToken != nil {
		if claims, ok := parseToken.Claims.(*Claims); ok && parseToken.Valid {
			return claims.Data, nil
		}
	}
	return "", err
}
